package com.itheima.pinda.filter;

import com.itheima.pinda.auth.client.properties.AuthClientProperties;
import com.itheima.pinda.auth.client.utils.JwtTokenClientUtils;
import com.itheima.pinda.auth.utils.JwtUserInfo;
import com.itheima.pinda.base.R;
import com.itheima.pinda.context.BaseContextConstants;
import com.itheima.pinda.exception.BizException;
import com.itheima.pinda.utils.StrHelper;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 当前过滤器负责解析请求头中的jwt令牌, 并且解析出的用户信息放入zuul的header中给后面的过滤器使用
 *
 * @author llhuang10
 * @date 2021/8/29 21:38
 */
@Component
public class TokenContextFilter extends BaseFilter {

    @Resource
    private AuthClientProperties authClientProperties;

    @Resource
    private JwtTokenClientUtils jwtTokenClientUtils;

    /**
     * 返回一个boolean类型来判断该过滤器是否要执行
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤逻辑
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        // 不进行拦截的地址
        if (super.isIgnoreToken()) {
            // true表示放行, 是一些静态资源或者swagger文档
            return null;
        }
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        // 这个请求头名字, 是根据前端提交的规则来取出来的
        // 先从请求头中获取前端提交的jwt令牌
        String userToken = request.getHeader(authClientProperties.getUser().getHeaderName());

        // 解析Jwt令牌, 可能会解析失败, 比如jwt被别人修改, 或为null
        JwtUserInfo userInfo = null;
        try {
            userInfo = jwtTokenClientUtils.getUserInfo(userToken);
        } catch (BizException ex) {
            // 自己的业务异常
            super.errorResponse(ex.getMessage(), ex.getCode(), 200);
            return null;
        } catch (Exception ex) {
            super.errorResponse("解析jwt令牌出错", R.FAIL_CODE, 200);
            return null;
        }
        // 将解析出来的用户信息放入到header中
        if (userInfo != null) {
            addHeader(ctx, BaseContextConstants.JWT_KEY_ACCOUNT,
                    userInfo.getAccount());
            addHeader(ctx, BaseContextConstants.JWT_KEY_USER_ID,
                    userInfo.getUserId());
            addHeader(ctx, BaseContextConstants.JWT_KEY_NAME,
                    userInfo.getName());
            addHeader(ctx, BaseContextConstants.JWT_KEY_ORG_ID,
                    userInfo.getOrgId());
            addHeader(ctx, BaseContextConstants.JWT_KEY_STATION_ID,
                    userInfo.getStationId());
        }

        return null;
    }


    /**
     * 前置过滤器
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 过滤器执行的顺序
     *
     * @return 数值越大, 优先级越低, 跟gateway的执行顺序一样
     */
    @Override
    public int filterOrder() {
        // 需要在这个PreDecorationFilter过滤器执行, 所以 order需要 + 1
        /**
         * 一定要在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter
         * 过滤器之后执行, 因为这个过滤器做了路由, 而我们需要这个路由信息来鉴权
         * 这个过滤器会将我们鉴权需要的信息放置在请求上下文中
         */
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
    }


    /**
     * 将指定信息放入zuul的header中
     *
     * @param ctx
     * @param name
     * @param value
     */
    private void addHeader(RequestContext ctx, String name, Object value) {
        // 注意下 springframework.util中的 SpringUtils和 apache中StringUtils
        // 这两个方法是不一样的, 区别在于 spring的参数是obj类型, 而apache是String类型
        if (StringUtils.isEmpty(value)) {
            return;
        }
        ctx.addZuulRequestHeader(name, StrHelper.encode(value.toString()));
    }
}
